package tcp

import (
	"errors"
	"net"
	"sync"
	"sync/atomic"
)

type ServerConfig struct {
	ListenAddr  string
	SendChanMax int
	RecvChanMax int
}

type Server struct {
	l             *net.TCPListener
	config        *ServerConfig
	callback      Callback
	acceptTimeout int
	exitChan      chan interface{}
	stopSync      sync.Once
	stopFlag      int32
	waitGroup     *sync.WaitGroup
}

func NewServer(callback Callback, config *ServerConfig) *Server {
	s := &Server{
		config:    config,
		callback:  callback,
		exitChan:  make(chan interface{}),
		waitGroup: &sync.WaitGroup{},
	}

	return s
}

func (s *Server) Start() error {
	if s.IsClosed() {
		return errors.New("use of closed tcp server")
	}

	tcpAddr, err := net.ResolveTCPAddr("tcp4", s.config.ListenAddr)
	if err != nil {
		return err
	}

	l, err := net.ListenTCP("tcp", tcpAddr)
	if err != nil {
		return err
	}

	go s.run(l)

	return nil
}

func (s *Server) IsClosed() bool {
	return atomic.LoadInt32(&s.stopFlag) == 1
}

func (s *Server) Close() {
	if atomic.LoadInt32(&s.stopFlag) == 0 {
		s.stopSync.Do(func() {
			atomic.StoreInt32(&s.stopFlag, 1)
			s.l.Close()
			close(s.exitChan)
			s.waitGroup.Wait()
		})
	}
	//fmt.Println("exit server")
}

func (s *Server) getCallback() Callback {
	return s.callback
}

func (s *Server) getExitChan() chan interface{} {
	return s.exitChan
}

func (s *Server) getSendChanMax() int {
	return s.config.SendChanMax
}

func (s *Server) getRecvChanMax() int {
	return s.config.RecvChanMax
}

func (s *Server) getWaitGroup() *sync.WaitGroup {
	return s.waitGroup
}

func (s *Server) run(l *net.TCPListener) {
	s.l = l
	s.waitGroup.Add(1)
	defer func() {
		l.Close()
		s.waitGroup.Done()
	}()

	for {
		select {
		case <-s.exitChan:
			//fmt.Println("server exit run")
			return
		default:
		}
		//l.SetDeadline(time.Now().Add(1 * time.Second))
		conn, err := l.AcceptTCP()
		if err != nil {
			continue
		}

		go func() {
			s.waitGroup.Add(1)
			defer func() {
				s.waitGroup.Done()
			}()

			go NewConn(conn, s).run()
		}()
	}
}
